package com.sneakxy.actions;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.Options;
import org.apache.commons.lang3.StringUtils;

import com.alibaba.fastjson.JSON;
import com.sneakxy.actions.code.ClassField;
import com.sneakxy.actions.code.ClassType;
import com.sneakxy.beetl.ActionTemplate;
import com.sneakxy.beetl.TemplateDir;
import com.sneakxy.config.Config;
import com.sneakxy.jdbc.Dialect;
import com.sneakxy.jdbc.JdbcHelper;
import com.sneakxy.jdbc.JdbcHelper.ColumnMetaDataHandler;
import com.sneakxy.utils.StringUtilsExt;

public class ActionCode implements Action {

	private ActionTemplate template;
	
	private JdbcHelper helper;
	
	private Config config;
	
	private Map<String, Object> params = new LinkedHashMap<>();
	
	private Map<String, Object> formatter = new LinkedHashMap<>();
	
	@Override
	@SuppressWarnings("unchecked")
	public void execute(CommandLine command, Options options) throws Exception {
		this.template = createActionTemplate(command, options);
		this.config = createConfig(command, options);
		this.helper = this.config.getJdbc().createJdbcHelper();
		try {
			helper.openConnection();
			//只有一个表
			if(StringUtils.isNotBlank(config.getCode().getTable())) {
				this.dealWithTable(helper.getDialect(), config.getCode().getTable());
			} else {
				List<String> tables = this.getTables(helper.getDialect());
				Iterator<String> it = tables.iterator();
				while(it.hasNext()) {
					this.dealWithTable(helper.getDialect(), it.next());
				}
			}
			params.put("config", config);
			
			Map<String, Object> map = JSON.parseObject(JSON.toJSONString(config.getProject()), Map.class);
			formatter.putAll(map);
			map = JSON.parseObject(JSON.toJSONString(config.getProject()), Map.class);
			formatter.putAll(map);
			
			TemplateDir baseDir = new TemplateDir("", config.getPath());
			baseDir.mkdirs();
			renderDirectory(this.template.getFile(), baseDir);
		} catch (ClassNotFoundException e) {
			System.out.println("驱动加载失败.");
		} catch (SQLException e) {
			System.out.println("数据库操作失败.");
			e.printStackTrace();
		} catch (Exception e) {
			System.out.println("代码生成失败.");
			e.printStackTrace();
		} finally {
			helper.close();
		}
	}
	
	protected List<String> getTables(Dialect dialect) throws SQLException {
		List<String> result = helper.execute(dialect.getTablesSql(), new JdbcHelper.ResultDealable<String>() {

			@Override
			public List<String> onResultSetCallback(ResultSet rs) throws SQLException {
				List<String> result = new ArrayList<>(); 
				while(rs.next()) {
					result.add(rs.getString(1));
				}
				return result;
			}
			
		});
		return result;
	}
	
	protected void dealWithTable(Dialect dialect, String tableName) throws SQLException {
		Set<ClassField> otherFields = this.getFields(dialect, tableName);
		List<ClassField> primaryKeys = this.getPrimaryKeyFields(dialect, tableName, otherFields);
		System.out.println(primaryKeys);
		String modelName = this.getModelName(tableName);
		this.params.put("module", config.getProject().getModule());
		this.params.put("fields", otherFields);
		this.params.put("primaryKeyFields", primaryKeys);
		this.params.put("modelName", modelName);
		String lowername = StringUtils.lowerCase(modelName);
		this.params.put("lowername", lowername);
		this.params.put("upername", StringUtilsExt.upperCaseFirst(lowername));
		String lowermoduel = StringUtils.lowerCase(config.getProject().getModule());
		this.params.put("lowermodule", lowermoduel);
		this.params.put("upermodule", StringUtilsExt.upperCaseFirst(lowermoduel));
		this.formatter.put("modelName", modelName);
		this.formatter.put("lowername", lowername);
	}
	
	protected Set<ClassField> getFields(Dialect dialect, String tableName) throws SQLException {
		final Set<ClassField> result = new LinkedHashSet<>();
		try {
			helper.columnMetaData(tableName, new ColumnMetaDataHandler() {
				
				@Override
				public void handler(String columnName, int columnType, String comment) {
					result.add(getField(columnName, columnType, comment));
				}
				
			});
		} catch (Exception e) {
			System.out.println("读取字段错误");
			e.printStackTrace();
		}
		return result;
	}
	
	protected Set<String> getPrimaryKeyName(Dialect dialect, String tableName) throws SQLException {
		Set<String> primaryKeyNames = new HashSet<>();
		ResultSet primaryKey = helper.getPrimaryKey(tableName);
		while(primaryKey.next()) {
			primaryKeyNames.add(primaryKey.getString("COLUMN_NAME"));
		}
		return primaryKeyNames;
	}
	
	protected List<ClassField> getPrimaryKeyFields(Dialect dialect, String tableName, Set<ClassField> fields) throws SQLException {
		Set<String> pkNames = getPrimaryKeyName(dialect, tableName);
		List<ClassField> result = new ArrayList<>();
		Iterator<ClassField> it = fields.iterator();
		ClassField field = null;
		while(it.hasNext()) {
			field = it.next();
			if(pkNames.contains(field.getExtra())) {
				result.add(field);
				it.remove();
			}
		}
		return result;
	}
	
	protected ClassField getField(String columnName, int columnType, String comment) {
		ClassField field = null;
		String fieldName =  StringUtilsExt.lowerCaseFirst(StringUtilsExt.formatStringName(columnName));
		field = new ClassField(new ClassType(JdbcHelper.getJavaType(columnType)), fieldName);
		field.setComment(comment);
		field.setExtra(columnName);
		field.setExtraType(columnType);
		return field;
	}
	
	protected String getModelName(String tableName) {
		String modelName = config.getCode().getName();
		if(StringUtils.isNotBlank(modelName)) {
			return StringUtils.trimToEmpty(modelName);
		}
		String table = tableName;
		if(StringUtils.contains(tableName, ".")) {
			table = StringUtils.split(tableName, ".")[1];
		}
		return StringUtilsExt.formatStringName(table);
	}
	
	@Override
	public Map<String, Object> getWriteParams() {
		return params;
	}

	@Override
	public Map<String, Object> getNameFormatter() {
		return formatter;
	}

	@Override
	public ActionTemplate getActionTemplate() {
		return template;
	}

	public boolean isAllowed(String name) {
		return StringUtils.endsWith(name, "[c]") || !StringUtils.endsWith(name, "[p]"); 
	}
	
}
